package com.example.steeringwheelseekbar

import android.content.Context
import android.graphics.PointF
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.ViewGroup
import android.widget.FrameLayout
import java.lang.Math.toDegrees
import kotlin.math.atan2
import kotlin.math.sqrt

class SteeringWheelSeekBar : FrameLayout {
    private var currentDegrees = 0f // 当前角度
    private var lastDegrees = 0f // 最后角度，用于经过档位去重判断
    private var listener: OnProgressChangedListener? = null // 角度改变的监听
    private var context: Context? = null
    private var foregroundView: SeekBarForegroundView? = null // 前景view
    private var foregroundSize = 0 // 图片的大小
    private var foregroundId = 0 // 前景图片id
    private var absoluteDegree = 0f // 绝对旋转角度
    private var centerPoint = PointF() // 中心点坐标
    private var pressPoint = PointF() // 按下点坐标
    private var movePoint = PointF() // 移动点坐标

    private var clockwiseLimitAngle: Int = Int.MAX_VALUE // 逆时针角度
    private var counterclockwiseLimitAngle: Int = Int.MIN_VALUE // 顺时针角度

    /**
     * 滑动接口
     */
    fun interface OnProgressChangedListener {
        fun onProgressChanged(level: Int)
    }

    constructor(context: Context) : super(context) {
        init()
    }

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        this.context = context
        val array = context.obtainStyledAttributes(attrs, R.styleable.SteeringWheelSeekBar)
        foregroundSize =
            array.getDimensionPixelSize(R.styleable.SteeringWheelSeekBar_foregroundSize, 90)
        foregroundId = array.getResourceId(R.styleable.SteeringWheelSeekBar_foreground, 0)
        array.recycle()
        init()
    }

    private fun init() {
        // 背景view
        val backgroundView = SeekBarBackgroundView(context!!)
        backgroundView.setLayoutParams(
            LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )
        )
        addView(backgroundView)
        foregroundView = SeekBarForegroundView(context!!, foregroundSize, foregroundId)
        addView(foregroundView)
    }

    // 设置最终档位监听
    fun setListener(listener: OnProgressChangedListener?): SteeringWheelSeekBar {
        this.listener = listener
        return this
    }

    // 设置旋转限制角度
    fun setLimitAngle(clockwise: Int, counterclockwise: Int): SteeringWheelSeekBar {
        clockwiseLimitAngle = clockwise
        counterclockwiseLimitAngle = counterclockwise
        return this
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        centerPoint[(right - left).toFloat() / 2] = (bottom - top).toFloat() / 2
        // 放置在父view的中间
        // 放置在父view的中间
        changeBallLayout((right - left) / 2, (bottom - top) / 2)
    }

    override fun performClick(): Boolean {
        return super.performClick()
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        when (event!!.action) {
            MotionEvent.ACTION_DOWN -> {
                pressPoint = getRelativelyCoordinate(PointF(event.x, event.y))
                return true
            }

            MotionEvent.ACTION_MOVE -> {
                movePoint = getRelativelyCoordinate(PointF(event.x, event.y))
                // 获取与按下点的相对夹角
                val relativelyDegree = getRotationDegrees()
                // 当前角度
                currentDegrees = absoluteDegree + relativelyDegree

                if (lastDegrees != currentDegrees) {
                    /*此处的300是对每次转角差值的一个大致范围定义，这个差值大小与系统刷新率有关，刷新率越快差值越小，我用模拟器测试差值最大是340左右
                    这里用300取商，对于多圈后误差会更大，理论上4圈以后就会出错，因为4圈后会计算为第5圈，因为每次除法都会有60度超出*/
//                    Log.e("TAG", "lastDegrees: $lastDegrees currentDegrees: $currentDegrees")
                    currentDegrees += 360 * ((lastDegrees - currentDegrees) / 300).toInt()
                    if (currentDegrees <= (clockwiseLimitAngle + 0.4f) && currentDegrees >= (counterclockwiseLimitAngle - 0.4f)) {
                        // 设置旋转角度
                        foregroundView?.setDegrees(currentDegrees)
                        listener?.onProgressChanged(currentDegrees.toInt())
                    } else if (currentDegrees > clockwiseLimitAngle) {
                        if (currentDegrees < lastDegrees) { // 当手势往回转时直接清空累计的转角
                            // 模拟手势抬起后又按下了的动作
                            currentDegrees = clockwiseLimitAngle.toFloat()
                            lastDegrees = currentDegrees
                            absoluteDegree = lastDegrees
                            pressPoint = getRelativelyCoordinate(PointF(event.x, event.y))
                        }
                    } else if (currentDegrees < counterclockwiseLimitAngle) {
                        if (currentDegrees > lastDegrees) { // 当手势往回转时直接清空累计的转角
                            // 模拟手势抬起后又按下了的动作
                            currentDegrees = counterclockwiseLimitAngle.toFloat()
                            lastDegrees = currentDegrees
                            absoluteDegree = lastDegrees
                            pressPoint = getRelativelyCoordinate(PointF(event.x, event.y))
                        }
                    }
                    lastDegrees = currentDegrees
//                    Log.e("TAG", "--lastDegrees: $lastDegrees currentDegrees: $currentDegrees")
                }
            }

            MotionEvent.ACTION_UP -> {
                movePoint = getRelativelyCoordinate(PointF(event.x, event.y))

                if (currentDegrees > clockwiseLimitAngle) {
                    lastDegrees = clockwiseLimitAngle.toFloat()
                } else if (currentDegrees < counterclockwiseLimitAngle) {
                    lastDegrees = counterclockwiseLimitAngle.toFloat()
                }

                absoluteDegree = lastDegrees
                performClick()
            }

            else -> {
                // 当手指移出View时，目前好像不会进入到这个case中
                Log.w("TAG", "onTouchEvent default: " + event.x + "," + event.y)
                movePoint[event.x] = event.y
                absoluteDegree += getRotationDegrees()
                if (listener != null) {
                    listener!!.onProgressChanged(absoluteDegree.toInt())
                }
            }
        }
        return super.onTouchEvent(event)
    }

    /**
     * 改变球的位置，将球在参数坐标处描画出来
     *
     * @param currentX 当前x坐标
     * @param currentY 当前Y坐标
     */
    private fun changeBallLayout(currentX: Int, currentY: Int) {
        foregroundView!!.layout(
            (currentX.toFloat() - foregroundView!!.measuredWidth / 2).toInt(),
            (currentY.toFloat() - foregroundView!!.measuredWidth / 2).toInt(),
            (currentX.toFloat() + foregroundView!!.measuredWidth / 2).toInt(),
            (currentY.toFloat() + foregroundView!!.measuredWidth / 2).toInt()
        )
    }

    /**
     * @return 返回相对于按下点的旋转角度
     */
    private fun getRotationDegrees(): Float {
        val pressPolar = getPolarCoordinate(pressPoint.x, pressPoint.y)
        val movePolar = getPolarCoordinate(movePoint.x, movePoint.y)
        // 相对转角
        var angle = (movePolar.second - pressPolar.second)
        if (angle < 0) angle += 360 // 将角度值调整到0-360的范围
        if (angle > 180) angle -= 360// 将角度值调整到-180-180的范围
        return angle
    }

    private fun getPolarCoordinate(x: Float, y: Float): Pair<Float, Float> {
        val radius = sqrt(x * x + y * y)
        var angle = atan2(y, x)
        // 将角度值调整到正确的范围
        if (angle < 0) {
            angle += 2f * Math.PI.toFloat()
        }
//        Log.e("TAG", "getPolarCoordinate: $radius ${toDegrees(angle.toDouble())}")
        return Pair(radius, toDegrees(angle.toDouble()).toFloat())
    }

    // 坐标相对中心点平移
    private fun getRelativelyCoordinate(point: PointF): PointF {
        return PointF(point.x - centerPoint.x, point.y - centerPoint.y)
    }

}